package com.adeng.springbootdemo.common;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author hzliubenlong
 *         <p>
 *         统一错误处理
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class ExceptionHandlerAdvice {

    /**
     * 通用未处理异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public WebResponse handleException(Exception e) {
        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.USER_ERROR, e.getMessage());
    }


    /**
     * 参数异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public WebResponse handleException(HttpMessageNotReadableException e) {
        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, "请检查您的参数类型!");
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public WebResponse handleException(MethodArgumentTypeMismatchException e) {
        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, e.getMessage());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public WebResponse handleException(HttpRequestMethodNotSupportedException e) {
        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, e.getMessage() + ". should be in " + JSONObject.toJSONString(e.getSupportedMethods()));
    }

    /**
     * 请求参数为空异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public WebResponse handleException(MissingServletRequestParameterException e) {
        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, e.getParameterName() + " 参数不可以为空");
    }

    /**
     * 参数为对象时的校验异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    public WebResponse handleException(BindException e) {
        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, JSONObject.toJSONString(e.getAllErrors()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public WebResponse handleException(MethodArgumentNotValidException e) {
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        List<JSONObject> collect = allErrors.stream().map(objectError -> {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("errorMsg", objectError.getDefaultMessage());
            String[] split = objectError.getCodes()[0].split("\\.");
            jsonObject.put("field", split[split.length - 1]);
            return jsonObject;
        }).collect(Collectors.toList());

        log.error(e.getMessage(), e);
        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, JSONObject.toJSONString(collect));
    }

    /**
     * 参数为普通属性时的校验异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public WebResponse handleIllegalParamException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        List<JSONObject> list = new ArrayList<>();

        constraintViolations.forEach(constraintViolation -> {
            JSONObject o = new JSONObject();
            o.put("errorMsg", constraintViolation.getMessage());
            o.put("field", constraintViolation.getPropertyPath().toString());
            o.put("value", constraintViolation.getInvalidValue().toString());
            list.add(o);
        });
        String errorMsg = JSONObject.toJSONString(list);
        log.error(errorMsg, e);

        return WebResponseUtils.response(ResponseCodeEnum.PARAMETER_ERROR, errorMsg);
    }
}